package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.JOptionPane;

import logic.PerformTransactions;
import ui.MessagePopUp;
import ui.Screen;
import data.Admin;
import data.BankDatabase;
import data.BankDatabaseAccountSQL;
import data.BankDatabaseUserSQL;
import data.RegexInput;

/** Represents the controller of the ATM,
 * and most of the even listeners of the 
 * application. Also, it containt the instances of 
 * classes
 *
 * @author Matthias
 * @version 1.2 19.05.13
 */
public class ATM {

	/** specifies whether user is authenticated	**/
	private boolean userAuthenticated;
	/** current user id of the authenticated user **/
	private int currentUserID;
	/** input of user choice of account transaction **/
	private int menuChoice;
	/** int object of the selected user id in a ui **/
	private int selectedUserID;
	/** declaring the Screen object **/
	private final Screen screen;
	/** declaring the BankDatabase object **/
	private final BankDatabase bankDatabase;
	/** declaring the BankDatabaseUserSQL object **/
	private final BankDatabaseUserSQL bdbUserSQL;
	/**	declaring the BankDatabaseAccountSQL **/
	private final BankDatabaseAccountSQL bdbAccountSQL;
	/** declaring the Admin object **/
	private final Admin admin; 
	/** declaring the PerformTransaction object **/
	private final PerformTransactions perform;
	/** declaring the ATMAdmControl object **/
	private final ATMAdmControl atmAdm;
	/** declaring the ATMUserMenuControl object **/
	private final ATMUserMenuControl atmUserMenu;
	/** declaring the RegexInput object **/
	private final RegexInput regexInput;

	/** The constructor of ATM initializes instance
	 * variables for the objects
	 */
	public ATM() {

		// user is not authenticated to start
		userAuthenticated = false; 
		// no current account number to start
		currentUserID = 0; 
		atmAdm = new ATMAdmControl(this);
		atmUserMenu = new ATMUserMenuControl(this);
		// create screen, the frame of the ATM
		screen = new Screen(this); 
		regexInput = new RegexInput();
		bankDatabase = new BankDatabase(this, regexInput); 
		// creates an unique admin for the ATM
		admin = new Admin(bankDatabase);
		// crate PerformTransactions, the logic link
		perform = new PerformTransactions(this);
		bdbUserSQL = new BankDatabaseUserSQL(bankDatabase);
		bdbAccountSQL = new BankDatabaseAccountSQL(bankDatabase);

		// Listener for Welcome GUI, the start screen
		screen.getWelcome().addActionListener(new LoginMenuActionListener());
	} 

	/**
	 * Adding guis and instantiating action listeners for the UIs
	 */
	public final void addUIs() {	

		// adding UIs which need the parameter of this class
		screen.addAdminDelete(this);
		screen.addAdminView(this);
		screen.addUserMenuDelete(this);
		screen.addChoiceAcc(this);
		screen.addUserMenuViewAcc(this);
		// Listener for all other UIs
		screen.getAdmMenu().addActionListener(new AdmMenuActionListener());
		screen.getAdmCreate().addActionListener(new AdmCreateActionListener());
		screen.getAdmEdit().addActionListener(new AdmEditActionListener());
		screen.getAdmEdit().addItemListener(new AdmEditActionListener());
		screen.getAdmDelete().addActionListener(new AdmDeleteActionListener());
		screen.getAdmView().addActionListener(new AdmViewActionListener());
		screen.getChoiceAcc().addActionListener(new ChoiceAccActionListener());
		screen.getAccMenu().addActionListener(new AccMenuActionListener());
		screen.getAccAccountInfo().addActionListener(new AccAccountInfoActionListener());
		screen.getAccDeposit().addActionListener(new AccDepositActionListener());
		screen.getAccWithdrawal().addActionListener(new AccWithdrawalActionListener());
		screen.getAccInterestRate().addActionListener(new AccInterestRateActionListener());
		screen.getUserMenu().addActionListener(new UserMenuActionListener());
		screen.getUserMenuAdd().addActionListener(new UserMenuAddAccountActionListener());
		screen.getUserMenuDelete().addActionListener(new UserMenuDeleteAccountActionListener());
		screen.getUserMenuViewAcc().addActionListener(new UserMenuViewAccActionListener());
	}

	/** starting the ATM here from the main class **/
	public final void run() {
		if (!userAuthenticated) {
			screen.show(Screen.WELCOME);
			screen.setVisible(true);
		}
	}

	/** Method checks whether the user is authenticated
	 * in the arraylist **/
	public final void authenticateUser() {
		
		try {
			int userID = 0;
			int pin = 0;

			// get the entered login data
			userID = screen.getWelcome().getLoginUserID();
			pin = screen.getWelcome().getLoginPin();

			// authenticating the user in the bankdatabase
			userAuthenticated =
					bankDatabase.authenticateUser(userID, pin);

			if (userAuthenticated) {
				currentUserID = userID; // save user's account
			} // end if
			else {
				String text = "You have either entered the wrong ID or PIN " 
						 + "\nPlease enter your correct data";
				new MessagePopUp().setError(text);
				screen.getWelcome().clearFields();
			}
		}
		catch (Exception exc) {
			String text = "Wrong input, please enter your correct data ";
			new MessagePopUp().setError(text);
		}
	} // end method authenticateUser

	///////////***** Listeners for the UIs and their appropriate components *****\\\\\\\\\
	///////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
 
	/** Welcome listener, for the Welcome ui.
	 * Defining the actions for only two buttons, 
	 * either exit or login
	 * @author Matthias
	 *
	 */
	private class LoginMenuActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getWelcome().getBtnExit()) {
				// answer of the user, if he wants to dispose the screen
				int selectedOption = JOptionPane.showConfirmDialog(
						null, "Are you sure you want to exit?", 
								"YES / NO", JOptionPane.YES_NO_OPTION); 
				if (selectedOption == JOptionPane.YES_OPTION) {
					screen.dispose();
				}
			} // if
			else {
				// authenticate user
				authenticateUser();
				if (userAuthenticated) {
					// user is authenticated, guis get initiated
					screen.initGUIs();
					addUIs();
					// if admin matches the id and pin of admin -> admin menu
					if (bankDatabase.getUsers(currentUserID).isToAdmin()) {
						screen.show(Screen.ADMMENU);
					}
					// if admin matches the id and pin of a user -> choice acc menu
					else if (!bankDatabase.getUsers(currentUserID).isToAdmin()) {	
						screen.getChoiceAcc().addAllAcc();
						// showing in the menu, the current user
						screen.getChoiceAcc().showField(
								currentUserID,		
								bankDatabase.getClassUser().getFirstName(),
								bankDatabase.getClassUser().getLastName()
								);
						screen.show(Screen.CHOICEACC);
					} // end else if
				} // end if
			} // end else
		} // end method 
	} // end inner class
 
	/** AdmMenu listener lets the admin to go the next 
	 * ui, depending on which he choses
	 * @author Matthias
	 *
	 */
	private class AdmMenuActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAdmMenu().getCreate()) {
				screen.show(Screen.ADMCREATE);
			}
			else if (e.getSource() == screen.getAdmMenu().getAdmEdit()) {
				screen.getAdmEdit().addAllUsers();
				screen.show(Screen.ADMEDIT);
			}
			else if (e.getSource() == screen.getAdmMenu().getDelete()) {
				screen.getAdmDelete().addAllUsers();
				screen.show(Screen.ADMDELETE);
			}
			else if (e.getSource() == screen.getAdmMenu().getView()) {
				screen.getAdmView().startCommand();
				screen.show(Screen.ADMVIEW);
			}
			else if (e.getSource() == screen.getAdmMenu().getLogout()) {
				int selectedOption = JOptionPane.showConfirmDialog(
						null, "Are you sure you want to logout?", 
								"YES / NO", JOptionPane.YES_NO_OPTION); 
				if (selectedOption == JOptionPane.YES_OPTION) {
					screen.getWelcome().clearFields();
					screen.show(Screen.WELCOME);	
				}
			}
		}
	}

	/** AdmCreate listener for creating users / customers
	 * @author Matthias
	 *
	 */
	private class AdmCreateActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAdmCreate().getSubmit()) {
				// creating the user in ATMAdmControl
				atmAdm.admCreate();
			}
			else if (e.getSource() == screen.getAdmCreate().getBack()) {
				screen.show(Screen.ADMMENU);
				screen.getAdmCreate().clearFields();
			}
		}
	}

	/** AdmEdit listener for editing users / customers, except
	 * the admin it self, which is not editable. The inner class
	 * is both having an action- and itemlistener
	 * @author Matthias
	 *
	 */
	private class AdmEditActionListener implements ActionListener, ItemListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAdmEdit().getBtnSubmit()) {
				atmAdm.admEdit();
			}
			else if (e.getSource() == screen.getAdmEdit().getBtnBack()) {
				screen.show(Screen.ADMMENU);
			}		
		}
		// itemlistener listens to the combobox items, when the selected
		// user changes, the data in the textfields change as well
		@Override
		public void itemStateChanged(final ItemEvent ie) {
			if (ie.getStateChange() == ItemEvent.SELECTED) {
				selectedUserID = screen.getAdmEdit().getSpecificUserID();
				// setting admin to non editable
				if (selectedUserID == 1234) {
					screen.getAdmEdit().setAdminNotEditable();
					screen.getAdmEdit().showUserDataFields(selectedUserID);
				} else {
					screen.getAdmEdit().setEditable();
					screen.getAdmEdit().showUserDataFields(selectedUserID);
				}
			}
		}
	}
 
	/** AdmDelete listener for deleting users / customers, except
	 * the admin it self, which is not deletable
	 * @author Matthias
	 *
	 */
	private class AdmDeleteActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAdmDelete().getSubmit()) {
				atmAdm.admDelete();
			}
			else if (e.getSource() == screen.getAdmDelete().getBack()) {
				screen.show(Screen.ADMMENU);
			}
		}
	}
 
	/** AdmView listener has only a reaction for the back button, 
	 * since the other part of the listener is in the ui class
	 * @author Matthias
	 *
	 */
	private class AdmViewActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAdmView().getBtnBack()) {
				screen.show(Screen.ADMMENU);
			}
		}
	}

	/** ChoiceAcc listener, having event listeners,
	 * for two buttons and one combobox, in which the user
	 * can select an account to proceed with. Or the user can choose
	 * to go to the user menu, managing his accounts
	 * @author Matthias
	 *
	 */
	private class ChoiceAccActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getChoiceAcc().getLogin()) {
				try {
					screen.getChoiceAcc().getComboBox().getSelectedItem().toString();
					screen.show(Screen.ACCMENU);
				} catch (Exception exc) {
					String text = "Error chosing an acccount";
					new MessagePopUp().setError(text);
				}
			}
			else if (e.getSource() == screen.getChoiceAcc().getUserMenu()) {
				screen.show(Screen.USERMENU);
			}
			else if (e.getSource() == screen.getChoiceAcc().getBack()) {
				int selectedOption = JOptionPane.showConfirmDialog(
						null, "Are you sure you want to logout?", 
								"YES / NO", JOptionPane.YES_NO_OPTION); 
				if (selectedOption == JOptionPane.YES_OPTION) {
					screen.show(Screen.WELCOME);
					screen.getWelcome().clearFields();
				}
			}
		}
	}

	/** AccMenu listener of all the buttons. The user / customer
	 * is having the choice, to enter four different ui, where he 
	 * will be able to perform transactions, if requirements are fulfilled,
	 * such as having a balance (see below)
	 * @author Matthias
	 *
	 */
	private class AccMenuActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			menuChoice = Integer.parseInt(e.getActionCommand());
			if (e.getSource() == screen.getAccMenu().getBtnDeposit()) {
				screen.getAccDeposit().
				// displaying the current total balance in the deposit ui
				showField(bankDatabase.getTotalBalance(currentUserID));
				screen.show(Screen.ACCDEPOSIT);	
				// clearing fields from start
				screen.getAccDeposit().clearField();
			}
			else if (e.getSource() == screen.getAccMenu().getBtnWithdrawal()) {
				// if the account has less equal than 0, the access is not granted
				// since no withdrawal would have been possible anyway
				if (bankDatabase.getTotalBalance(currentUserID) <= 0) {
					String text = "There is currently no money on the bank"
							+ "account. Please do a deposit first";
					new MessagePopUp().setError(text);
				}
				else {
					// if total balance is > 0, access is granted
					screen.getAccWithdrawal().
					showField(bankDatabase.getTotalBalance(currentUserID));
					screen.getAccWithdrawal().setRdbtnsTrue();
					screen.show(Screen.ACCWITHDRAWAL);
				}
			} 
			else if (e.getSource() == screen.getAccMenu().getBtnAccountInfo()) {
				screen.show(Screen.ACCACCOUNTINFO);
			} 
			else if (e.getSource() == screen.getAccMenu().getBtnInterestRate()) {
				// again, no access granted, when total balance is less equal than 0
				if (bankDatabase.getTotalBalance(currentUserID) <= 0) {
					String text = "There is currently no money on the bank"
							+ "account. Please do a deposit first";
					new MessagePopUp().setError(text);
				}
				// if money is available, access is granted
				else {
					screen.getAccInterestRate().clearField();
					screen.show(Screen.ACCINTERESTRATE);
				}
			}
			else if (e.getSource() == screen.getAccMenu().getBtnBack()) {
				screen.getChoiceAcc().addAllAcc();
				screen.show(Screen.CHOICEACC);
			}
			else if (e.getSource() == screen.getAccMenu().getBtnLogout()) {
				int selectedOption = JOptionPane.showConfirmDialog(
						null, "Are you sure you want to logout ?", 
								"YES / NO", JOptionPane.YES_NO_OPTION); 
				if (selectedOption == JOptionPane.YES_OPTION) {
					screen.show(Screen.WELCOME);
					screen.getWelcome().clearFields();
				}
			}
		}
	}

	/** AccAccountInfo listener, displaying, and retrieving all infos of
	 * the account in the text fields, when a button is clicked
	 * @author Matthias
	 *
	 */
	private class AccAccountInfoActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			screen.getAccAccountInfo().clearFields();
			if (e.getSource() == screen.getAccAccountInfo().getBtnAccountInfo()) {
				// proceed to PeformTransactions
				perform.performTransactions(menuChoice);
			}
			else if (e.getSource() == screen.getAccAccountInfo().getBtnBack()) {
				// going back to the account menu
				screen.show(Screen.ACCMENU);
			}
		}
	}

	/** AccDeposit listener, depositing money, when a button 
	 * is clicked
	 * @author Matthias
	 *
	 */
	private class AccDepositActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAccDeposit().getBtnDeposit()) {
				try {
					// proceed to PeformTransactions
					perform.performTransactions(menuChoice);
				} catch (Exception exc) {
					String text = "No valid input! Please enter"
							 + " a number!";
					new MessagePopUp().setError(text);
				}
			}
			else if (e.getSource() == screen.getAccDeposit().getBack()) {
				screen.show(Screen.ACCMENU);
			}
		}
	}
 
	/** AccWithdrawal listener withdrawing money from an account
	 * if a button is clicked. Can either withdraw thourgh rdbtns or
	 * input dialog box
	 * @author Matthias
	 *
	 */
	private class AccWithdrawalActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAccWithdrawal().getBtnWithdrawal()) {
				try {
					perform.performTransactions(menuChoice);	
				} catch (Exception exc) {
				}
			}
			else if (e.getSource() == screen.getAccWithdrawal().getBtnCustomAmount()) {
				screen.getAccWithdrawal().showDialog();				
			}
			else if (e.getSource() == screen.getAccWithdrawal().getBtnBack()) {
				screen.show(Screen.ACCMENU);
			}
		}
	}
 
	/** AccInterestRate listener calculating 
	 * the for an account, by pressing a button and
	 * taking the input from the textfield
	 * @author Matthias
	 */
	private class AccInterestRateActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getAccInterestRate().getBtnCalculate()) {
				screen.show(Screen.ACCINTERESTRATE);
				perform.performTransactions(menuChoice);
				// displaying the calculated interest rate,
				// instead go back to account menu
				screen.show(Screen.ACCINTERESTRATE);
			}
			else if (e.getSource() == screen.getAccInterestRate().getBtnBack()) {
				screen.show(Screen.ACCMENU);
			}
		}
	}

	/** UserMenu listener, containing only button
	 * in order to proceed to another ui, or logging out
	 * @author Matthias
	 *
	 */
	private class UserMenuActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getUserMenu().getBtnCreate()) {
				// restricting the user, add more accounts than 3
				if (bankDatabase.getUsers(currentUserID).getAccountList().size() >= 3) {
					String text = "You already have already" 
							+ "\n 3 Accounts, no more allowed";
					new MessagePopUp().setError(text);
				} else {
					// less than 3 -> access allowed
					screen.show(Screen.USERMENUADD);
				}
			}
			else if (e.getSource() == screen.getUserMenu().getBtnDelete()) {
				// restricting the user access to ui of UserMenuDelete,
				// when no account is available
				if (bankDatabase.getClassUser().getAccountList().size() <= 0) {
					String text = "Currently, you have no account available!";
					new MessagePopUp().setError(text);
				} else {
					screen.getUserMenuDelete().addAllAcc();
					screen.show(Screen.USERMENUDELETE);
				}
			}
			else if (e.getSource() == screen.getUserMenu().getBtnView()) {
				// displaying all the account data of the current user
				screen.getUserMenuViewAcc().displayQueryResults(
						screen.getUserMenuViewAcc().getQuery());
				screen.show(Screen.USERMENUVIEWACC);
			}
			else if (e.getSource() == screen.getUserMenu().getBtnBack()) {
				screen.getChoiceAcc().addAllAcc();
				screen.show(Screen.CHOICEACC);
			}
		}
	}

	/** UserMenuAddAccount listener, if button submit 
	 * is clicked, an account is added to the users storage
	 * @author Matthias
	 *
	 */
	private class UserMenuAddAccountActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getUserMenuAdd().getBtnSubmit()) {
				// proceeding to ATMUserMenuControl, where the action is continued
				atmUserMenu.userMenuAdd();
			} // end if
			else if (e.getSource() == screen.getUserMenuAdd().getBtnBack()) {
				screen.show(Screen.USERMENU);
				screen.getUserMenuAdd().clearFields();
			}
		}
	}

	/** UserMenuDeleteAccount event listener, if button submit 
	 * is clicked, an account is deleted of the users storage
	 * @author Matthias
	 *
	 */
	private class UserMenuDeleteAccountActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getUserMenuDelete().getBtnSubmit()) {
				// proceeding to ATMUserMenuControl, where the action is continued
				atmUserMenu.userMenuDelete();
			}
			else if (e.getSource() == screen.getUserMenuDelete().getBtnBack()) {
				screen.show(Screen.USERMENU);
			}
		}
	}

	/** UserMenuViewAcc listener, viewing all the
	 * data of the users accounts from the database. 
	 * No actions other than going back to the User Menu
	 * are availabe here.
	 * @author Matthias
	 *
	 */
	private class UserMenuViewAccActionListener implements ActionListener {
		@Override
		public void actionPerformed(final ActionEvent e) {
			if (e.getSource() == screen.getUserMenuViewAcc().getBtnBack()) {
				screen.show(Screen.USERMENU);
			}
		}
	}

	/**
	 * @return BankDatabase object
	 */
	public final BankDatabase getClassBankDatabase() {
		return bankDatabase;
	}

	/**
	 * @return Screen object
	 */
	public final Screen getClassScreen() {
		return screen;
	}

	/**
	 * @return PerformTransactions object
	 */
	public final PerformTransactions getClassPerformTransactions() {
		return perform;
	}

	/**
	 * @return BankDatabaseUserSQL object
	 */
	public final BankDatabaseUserSQL getClassBdbUserSQL() {
		return bdbUserSQL;
	}

	/**
	 * @return BankDatabaseAccountSQL object
	 */
	public final BankDatabaseAccountSQL getClassBdbAccSQL() {
		return bdbAccountSQL;
	}

	/**
	 * @return selectedUserID in the AdmEdit,
	 * which are selected by the admin in the combobox
	 */
	public final int getSelectedUserID() {
		return selectedUserID;
	}

	/**
	 * @return currentUserID, the current user which
	 * is logged in
	 */
	public final int getCurrentUserID() {
		return currentUserID;
	}

} //end of ATM class